#ifndef __case_tcp_kenny_kfjiweur89u83r33e3e__
#define __case_tcp_kenny_kfjiweur89u83r33e3e__

#include <typeinfo>

// 日志组件
#include <hude/base/log.hpp>
// 十六进制组件
#include <hude/base/hex.hpp>
// 网络IO组件
#include <hude/aio/network.hpp>

#include "test/test.hpp"

namespace test
{
    enum : int { emCOUNT = 100 };

    using namespace hude;
  
    /*********************************************************************
     * @brief 定义网络工具集
     */
    using net_t = hude::net::net_t<>;

    /*********************************************************************
     * @brief 该示例建立一对TCP通信，由客户端发起一桢数据，
     * 之后服务端与客户端互相回显该数据，并且每次回显少发一个字节，直到没有数据可回显。
     */
    struct case_tcp_t : public hude::test::case_t
    {
        /*********************************************************************
         * @brief 测试结果
         */
        int result;

        /*********************************************************************
         * @brief 声明IO上下文
         */
        net_t::ctx_t aio;

        /*********************************************************************
         * @brief case_tcp_listen_t
         */
        case_tcp_t() : result( 0 ) {}

        /*********************************************************************
         * @brief run
         */
        bool run() override
        {
            enum { emPORT = 2222U };

            //声明IO上下文, Linux默认是epoll，Windows默认是select
            aio = hude::aio::context_t<>::ctx();
    
            auto cb_entry = [=]
            {
                tcp_server( aio, emPORT );
                std::this_thread::yield();
                tcp_client( aio, emPORT );
            };

            // 事件循环，会阻塞直到退出或错误
            auto ret = aio->run( cb_entry );

            if( ret < 0 )
            {
                log_err( "aio error( %d ): %s", ret, aio->error() );
            }

            return result == 0;
        }
    
        /*********************************************************************
         * @brief 数据接收事件的处理回调
         */
        void on_read( net_t::sock_t chl )
        {
            llog_tra( "<case-tcp>", "\t %s::%s()", typeid( *this ).name(), __func__ );

            // 数据接收缓存
            hude::buffer_t buffer;

            // 接收数据，返回值-1为没有数据可接收，0为网络断开了
            auto ret = chl->recv( buffer );
            if( ret != sizeof(int) ) return;

            int* index = (int*)(void*)&(buffer[0]);

            // 打印数据
            log_inf( "\t socket read index: %d", *index );

            if( *index != -1 )
            {
                *index = -1;

                chl->send( buffer );
            } else {
                this->result -= 1;
            }

            chl->close();
        }
    
        /*********************************************************************
         * @brief 网络关闭事件的处理回调
         */
        void on_close( int index, net_t::sock_t sock )
        {
            llog_tra( "<case-tcp>", "\t %s::%s()", typeid( *this ).name(), __func__ );

            log_nte( "<index %d> socket closed: %d", index, **sock );
        }
  
        /*********************************************************************
         * @brief TCP服务端测试
         */
        void tcp_server( net_t::ctx_t aio, uint16_t port )
        {
            // 连接请求事件处理
            auto cb_accepted = [=]( const net_t::address_t& host, net_t::sock_t sock )
            {
                log_nte( "accepted client{ sock: %d, host: %s }", **sock, host.str().c_str() );

                // 绑定数据接收和关闭事件的处理回调
                sock->on_recv_  = std::bind( &case_tcp_t::on_read,  this, std::placeholders::_1 );
                sock->on_close_ = std::bind( &case_tcp_t::on_close, this, -1, std::placeholders::_1 );
            };

            // 服务端开始监听端口
            auto service = net_t::io_obj< net_t::tcp_listen_t >( aio, "::", port, cb_accepted );

            // 监听端口的关闭事件
            service->on_close_ = std::bind( &case_tcp_t::on_close, this, 0, std::placeholders::_1 );

            bool state = service->state();

            if( ! state )
            {
                log_err( "socket error: %s", service->error() );
                service->close();

                this->result = -100;
            }
        }
 
        /*********************************************************************
        * @brief TCP客户端测试
        */
        void tcp_client( net_t::ctx_t aio, uint16_t port )
        {
            // 连接请求事件处理
            auto cb_ready = [=]( int index, net_t::sock_t sock )
            {
                log_nte( "<index %d> connectd sock{ sock: %d }", index, **sock );

                // 绑定数据接收事件的处理回调
                sock->on_recv_  = std::bind( &case_tcp_t::on_read,  this, std::placeholders::_1 );

                // 发送数据
                const char* body = (const char*)(const void*)(&index);
                sock->send( body, sizeof(index) );

                this->result += 1;
            };

            auto cb_clients = [=]
            {
                hude::procbar_t pbar;

                for( int i = 0; i < emCOUNT; ++i )
                {
                    int index = i + 1;

                    // 客户端发起连接, 并加入epoll监听。
                    // -- 默认收到第一次EPOLLOUT事件后不再监听该事件.
                    // -- 如果要持续关注EPOLLOUT事件可在ready事件处理中修改： sock->attch( EPOLLIN | EPOLLOUT | EPOLLET )
                    auto service = net_t::io_obj< net_t::tcp_connect_t >(
                        aio, "::1", port, std::bind( cb_ready, index, std::placeholders::_1 ) );

                    // 绑定网络关闭事件的处理回调
                    service->on_close_ = std::bind( &case_tcp_t::on_close, this, index, std::placeholders::_1 );

                    bool state = service->state();

                    if( ! state )
                    {
                        log_err( "socket error: %s", service->error() );
                        service->close();

                        this->result = -100;
                    }

                    pbar( index * ( 100.0f / emCOUNT ), hude::format_t( "<index %d> tcp client connect.", index )->c_str() );

                    std::this_thread::sleep_for( std::chrono::milliseconds( 50U ) );
                }

                aio->cancel();
            };

            aio->task_commit( cb_clients );
        }
    };

    //--------------------------------------------------------------------------------
    TEST_CASE_ADD( "case-tcp", case_tcp_t, false );

} // namespace test

#endif //__case_tcp_kenny_kfjiweur89u83r33e3e__

